'''
UNIT INFO
Module for importing and exporting ('thru-porting') text-based files

ADAP HIS
-provide support for inter-os thruport of text-files 
(considering line-break-differences)
    -replace \n with flexible linesepst 
    -adjust calling functions
        -search adapted function with lineseptypest
            search callers
                add parameter
                    -for now static, later based on dialog-options
    v-special adapts at import because the clipping of separators is 
    dependent on the length
        getlistfromtextfile
        importtreefile
        importchange....
    v-realize data export generic
    v-testing
        v-the mobile exports and imports
        v-the generics
    -add dialog-options and parametrize params
        v-exportpanel
        v-importpanel
            v-adap defgui
        v-thruportpanel
    -retest all interports


ADAP NO

'''


import os
import sys
from datetime import datetime
import defgui   # **specific**
##import jodb
import pardb
import knob_logic
import wx
import jotools
import os_species


def exportDataToFile(queryst,folderpathst, filenamest, lineseptypest):
    '''
    UNIT INFO:
    Do some of the following steps:

    1) exports tasks to a text-file something like:
        _za _12-05 14:00_object2                       _rel_object1             _        _2009-01-05_     _                    _
        _do _12-08 16:00_fiets                         _x  _kopen               _        _2009-01-05_     _                    _
     px _   _           _huisraad                      _y  _kopen               _        _2009-01-05_     _                    _

    These tasks are records with either a non-null date or change-field

    2) exports multiple branches based on change = 'xp':
    kwal  a     _http://www.nationalevacatureba_kwal.functionaris - _    _file-pat_           _0  _3353 _
    medi  a     _rekenen met samenwonen        _media-server install_    _0       _           _0  _3212 _
    medi  b     _gewenste aansluitingen        _media-server install_    _1       _           _0  _3204 _
    medi  ba    _uitgang naar beeldscherm      _gewenste aansluiting_    _0    _           _1  _3211 _

    3) recreate a new template for import-file based on change = xp

    ADAP HIS
    -create special tasks-export-query
    -make a dict in defgui with the widths of each field
        -query-name
            -headings
            -field-widths
    -use ljust and slicing to make the strings of equal size
    -test on mobile fone
    -export certain branches based on a certain change-field like 'xp'
        v-make a wrapper around readBrancheToTable based on these
        'xp'-marked branches
        -complete the exportData function
            v-make query from relationstemp
            v-test the multibranche-function
            v-copy and edit the task-code
    -adapt the query for branches to 'hide'
    -add lineseptypest for line separator

    ADAP NO

    ADAP FUT
    -implement:
        -file-selection
        -headers
    -export-file-format:
        -add week??; or too little space for that? maybe as a last column
    '''

    normPath = os.path.normpath(folderpathst)
    filePath = os.path.join(normPath, filenamest)
    lsepst = os_species.getLineSeparator(lineseptypest) # line-separator
    

    try:
        fsock = open(filePath,'w')
    except IOError: # handle this specific error here
        customst = 'File or path not found.'
        messagest = customst +  '\n\n' + str(sys.exc_info()[1])
        print messagest
##        showMessage(self, messagest, str(sys.exc_info()[0]),
##            wx.OK | wx.ICON_EXCLAMATION)
        raise
    except:
        messt = 'An unhandled error occured; operation ended.\n\n' \
             + str(sys.exc_info()[1])
        print messt
##        showMessage(self, messt, str(sys.exc_info()[0]),
##            wx.OK | wx.ICON_ERROR)
        raise UnhandledError, 'Unhandled error...'
    else:
        try:
            if queryst=='FDQ_RELATIONS_EXPORTABLE':
                cursorli = pardb.readFromParams('', queryst,None, None)
                for rowli in cursorli:
                    fsock.write(lsepst)
                    fsock.write('0.' + rowli[4])
                    fsock.write(lsepst)
            else:
                if queryst=='FDQ_RELS_MULTIBRANCHE_EXPORT':
                    knob_logic.readMultipleBranchesToTable('xp')

                queryfieldli = defgui.defTexchangedi[queryst]

                cursorli = pardb.readFromParams('', queryst,None, None)
                for row in cursorli:
                    it = 0
                    for val in row:
                        skipbo = False
                        # ---------------
                        # remove the 'nones'
                        if str(val) == 'None': valst = ''
                        else: valst = str(val)
                        # ---------------
                        stringlengthit = queryfieldli[it][1]
                        if stringlengthit == -1:    # skip these fields
                            skipbo = True
                        else:
                            valst = valst[:stringlengthit].ljust(stringlengthit)
                        if not skipbo:
                            fsock.write(valst)
                            fsock.write('_')
                        it = it + 1
                    fsock.write(lsepst)
        finally:
            fsock.close()


def importTreeFile(folderpathst, filenamest, lineseptypest):
    '''
    UNIT INFO
    -read import-file and create tree-linked database-records
    Sample-text-file-input (remark the different pos. date-formats):
        0.existing node
        1.rel1.bike.px.8-12-05 16:00
        1.rel2.stuff.px.
        2.x.kannetje..5-12 14:00
        3.y.langs blokker.p.
        3.z.koffie meenemen..2005-12-23 15:00
        1.z.tandpasta.ko.

    ADAP HIS
    -remove the \n's
    -test with some file-layouts
    -add the other fields, change and msdate, to the test-code
    -test-write to db
        -empty date-fields are not written to db
    -for now make the 1st dot unnecessary by equalizing all levels.
    -after import import-file:
            v-backup latest file
                -create/seek test-project for file-linked operations
                    -try to copy a file
            v-create new one and auto-fill import-file with the headers
                -try to do this in export-branch function
    -test for after-dots

    ADAP NO

    ADAP FUT
    -update the params for the gui-started thruports?? or: redesign the
    dialog

    -resolve current restriction-breaks:
        v-no after-dots
        -non-field-linked dots (ex. for shortening)
        -no spaces before and after lines

    -improvements:
        -simplify text-file,
            -still to assess:
                -to shorten the text-file some level-codes might be
                left away and automatically forehung.
            -aft-place missing dots
        -pre-validate the file
    '''

    normPath = os.path.normpath(folderpathst)
    filePath = os.path.join(normPath, filenamest)
    curtimest = str(datetime.today())[:19]
    lsepst = os_species.getLineSeparator(lineseptypest) # line-separator


    try:
        fsock = open(filePath,'r')
        print '=====file opened \n'
        print fsock
    except IOError: # handle this specific error here
        customst = 'File or path not found.'
        messagest = customst +  '\n\n' + str(sys.exc_info()[1])
        print messagest
        raise
    except:
        messt = 'An unhandled error occured; operation ended.\n\n' \
             + str(sys.exc_info()[1])
        print messt
        raise UnhandledError, 'Quiting application...'
    else:
        try:
            # transfer file to list-variables
            fileli = []
            brancheli = []
            firstFullLineReached = False    # to enable more whitelines

            linecountit = 0
            lineposit = 0

            # determine number of lines
            for elem in fsock:
                linecountit = linecountit + 1
            fsock.seek(0,0)     # reset pointer to start-position

            print 'linecountit = ' + str(linecountit)

            for linest in fsock:
                lineposit = lineposit + 1
                print linest
                if linest <> lsepst:
                    firstFullLineReached = True
                    # remove \n's when present
                    if linest[len(linest)-len(lsepst):]==lsepst:
                        linest = linest[0:len(linest)-len(lsepst)]
                    # listalize..
                    lineli = linest.split('.')
                    print lineli
                    brancheli.append(lineli)
                if firstFullLineReached and (linest == lsepst or
                        lineposit == linecountit):
                    # blank lines indicate new branche
                    fileli.append(brancheli)
                    brancheli = []
                    firstFullLineReached = False

            print '========fileli =' + str(fileli)

            # write data to db
            testbo = False
            for brancheli in fileli:
                for lineli in brancheli:
                    levelit = lineli[0]
                    curindexit = brancheli.index(lineli)
                    if curindexit > 0:
                        if testbo:
                            print str(getLastUpperObject(
                                    curindexit, brancheli))
                            print '.' + str(lineli[1])
                            print '.' + str(lineli[2])
                            if len(lineli) > 3:
                                print '.' + str(lineli[3])
                            if len(lineli) > 4: # last fields or dots present
                                print '.' + str(lineli[4]) + '\n'
                        else:
                            if len(lineli) == 3:
                                pardb.addnewFromParams('', 'Relations',
                                    {
                                    "Object1":getLastUpperObject(curindexit, brancheli),
                                    "Relation":lineli[1],
                                    "Object2":lineli[2],
                                    "ModDate":curtimest
                                    })

                            elif len(lineli) == 4 or  lineli[4] == '':
                                pardb.addnewFromParams('', 'Relations',
                                    {
                                    "Object1":getLastUpperObject(curindexit, brancheli),
                                    "Relation":lineli[1],
                                    "Object2":lineli[2],
                                    "Change":lineli[3],
                                    "ModDate":curtimest
                                    })
                            elif len(lineli) == 5:
                                pardb.addnewFromParams('', 'Relations',
                                    {
                                    "Object1":getLastUpperObject(curindexit, brancheli),
                                    "Relation":lineli[1],
                                    "Object2":lineli[2],
                                    "Change":lineli[3],
                                    "MsDate":lineli[4],
                                    "ModDate":curtimest
                                    })
        finally:
            fsock.close()
            jotools.backupFile(filePath)



def getLastUpperObject(curindexit, brancheli):
    curlevelit = int(brancheli[curindexit][0])
    print 'curlevel = ' + str(curlevelit)
    for it in range(curindexit-1, -1,-1):
        print 'it = ' + str(it)
        if brancheli[it][0] == str(curlevelit - 1):
            if curlevelit == 1:
                objectst = brancheli[it][1]
            else:
                objectst = brancheli[it][2]
            break
    return objectst

def importChangesFromExportFiles(folderpathst, filenamest):
    # read the lines
    # if the first symbol is change-marker then
    #   -retrieve the RelID from the end of the line
    #   -update the record with that id to change='h'

    normPath = os.path.normpath(folderpathst)
    filePath = os.path.join(normPath, filenamest)

    try:
        fsock = open(filePath,'r')
        print '=====file opened \n'
        print fsock
    except IOError: # handle this specific error here
        customst = 'File or path not found.'
        messagest = customst +  '\n\n' + str(sys.exc_info()[1])
        print messagest
        raise
    except:
        messt = 'An unhandled error occured; operation ended.\n\n' \
             + str(sys.exc_info()[1])
        print messt
        raise UnhandledError, 'Quiting application...'
    else:
        try:
            changemarkerst = '..'
            # read the lines
            for linest in fsock:
##                print linest[:10]
                #------------------
                # if the first symbol is two dots (change-marker) then
                if linest[:2]== changemarkerst:
                    # retrieve the RelID from the end of the line
                    linetailst = linest[len(linest)-8:][:6]
                    print linetailst
                    relidit = int(linetailst)
                    print str(relidit)
                    # update the record with that id to change='h'
                    pardb.updateFromParams('', 'Relations',
                        {'strings':{'Change':'h'}},
                        {'numbers':{'RelID':relidit}})
        except:
            raise
        finally:
            fsock.close()


def exportGeneric(exportfolderst, exportfilest,
                    deffolderst, deffilest,
                    queryst, additionalcritdi = None, lineseptypest = ''):


    '''
    ADAP NO
    -use calling-loc for planning
    '''

    normPath = os.path.normpath(exportfolderst)
    expfilepath = os.path.join(normPath, exportfilest)
    separatorst = knob_logic.readOption('column_separator','sys')
    lsepst = os_species.getLineSeparator(lineseptypest) # line-separator


    try:
        fsock = open(expfilepath,'w')
    except IOError: # handle this specific error here
        customst = 'File or path not found.'
        messagest = customst +  '\n\n' + str(sys.exc_info()[1])
        print messagest
        raise
    except:
        messt = 'An unhandled error occured; operation ended.\n\n' \
             + str(sys.exc_info()[1])
        print messt
##        showMessage(self, messt, str(sys.exc_info()[0]),
##            wx.OK | wx.ICON_ERROR)
        raise
    else:
        try:
##            if queryst=='FDQ_RELS_MULTIBRANCHE_EXPORT':
##                knob_logic.readMultipleBranchesToTable('xp')

            defpath = os.path.join(os.path.normpath(deffolderst), deffilest)
            queryfieldli = getListFromTextFile( defpath, ',',
                                    lineseptypest = lineseptypest)
            cursorli = pardb.readFromParams('', queryst,None, additionalcritdi)

            # create on top one header-row
            for fieldli in queryfieldli:
                fieldst = fieldli[0]
                colwidthit = int(fieldli[1])
                if colwidthit <> -1:
                    fieldst = fieldst[:colwidthit].ljust(colwidthit)
                    fsock.write(fieldst)
                    fsock.write(separatorst)
            fsock.write(lsepst)

            # append data-rows
            for row in cursorli:
                colit = 0
                for val in row:
                    skipbo = False
                    # ---------------
                    # remove the 'nones'
                    if str(val) == 'None': valst = ''
                    else: valst = str(val)
                    # ---------------
                    stringlengthit = int(queryfieldli[colit][1])
                    if stringlengthit == -1:    # skip these fields
                        skipbo = True
                    else:
                        valst = valst[:stringlengthit].ljust(stringlengthit)
                    if not skipbo:
                        fsock.write(valst)
                        fsock.write(separatorst)
                    colit = colit + 1
                fsock.write(lsepst)

        except:
            raise
        finally:
            try:
                fsock.close()
            except:
                pass


def getListFromTextFile(filepathst, separatorst,
        removeboundaryspacesbo=False, striplastitembo = False, lineseptypest=''):
    '''
    UNIT INFO
    -create a 2-dimensional list from the file.
    -params
        -remove trailing spaces
        -strip the last empty list-item which is based on the final separator
    '''
    
    lsepst = os_species.getLineSeparator(lineseptypest) # line-separator
    print '------------******-----------'
    print lsepst
    print str(lsepst)
    print len(str(lsepst))
    print len(lsepst)
    print '------------*******-----------'
    
    try:
        print filepathst
        fsock = open(filepathst, 'r')
    except:
        raise

    else:
        fileli = []
        for linest in fsock:

##            print linest
            if linest <> lsepst:
                # remove line-seps when present
                if linest[len(linest)-len(lsepst):]==lsepst:
                    linest = linest[0:len(linest)-len(lsepst)]
                # listalize..
                lineli = linest.split(separatorst)
                if removeboundaryspacesbo:
                    lineli = [elem.strip() for elem in lineli]
                if striplastitembo:
                    lineli = lineli[:len(lineli)-len(lsepst)]
##                print lineli
                fileli.append(lineli)

##        print fileli
    return fileli




def importGeneric(importfilepathst, projecst, importedfromst,
        colseparatorst, lineseptypest):

    '''
    UNIT INFO
    Import a text-file of fitting format into the current db.
    Procedure updated from 2.7 upward using new database-fields.

    ADAP HIS
    -create new import-stem in db
        -import_timestamp (ob2) related to imported files (ob1)
    -read the lines
        -two-passes:
            -ob1 linked to imp-timestamp
            -ob2 linked to ob1

    -remove the prior coat-stand for the imported lines
    -add extra fields project, importedfrom and importdate
    '''

    imfilenamest = os.path.basename(importfilepathst)
    curtimest = str(datetime.today())[:19]

    fileli = getListFromTextFile(importfilepathst, colseparatorst, 
        True, True, lineseptypest)
    choppedheaderli = fileli[0]
##    print choppedheaderli
    samplefieldli = pardb.getFieldList('','RELATIONS')
##    print samplefieldli
    fullheaderli = broadenHeaderList(choppedheaderli, samplefieldli )
##    print  'fullheaderli:\n' + str(fullheaderli)

    # below relid handled by database, other fields are import-identifiers
    excludablefieldsli = ['RelID','Project','ImportedFrom','ImportDate']
    print 'main import-fase'
    linenumit = 0

    if fullheaderli <> [] and len(fileli) > 1:  # a headerline and >= 1 data-line
        for lineli in fileli:
            if linenumit > 0:   # skip header
                contentdi = {}  # contains dict with the actual lines
                colit = 0
                contentdi['Project']= projecst
                contentdi['ImportedFrom']= importedfromst
                contentdi['ImportDate']= curtimest

                for valst in lineli:
                    if fullheaderli[colit] not in excludablefieldsli:
                        if fullheaderli[colit] in samplefieldli:
                            if valst <> '': # dont import empty fields
                                contentdi[fullheaderli[colit]] = str(valst)
                    colit += 1
    ##            print contentdi
                pardb.addnewFromParams('', 'RELATIONS', contentdi)

            linenumit += 1

        print 'import ended'
        return True     # import starts
    else:
        return False    # import interrupted



def broadenHeaderList(smallheaderli, fullheaderli):
    '''
    Broaden headers of a partial headers-list to full headers (for field-names)
    using a (differently sorted) sample-header-list, and return the complete list.
    (beware: headers can be non-field elements, such as 'week' which is a
    derived element).
    Upd. depth-field interferes unwantedly with depth_left and depth_right
    '''

    broadenedlist = []
    for smallst in smallheaderli:
        realfieldbo = False
        for fullst in fullheaderli:
            if fullst.startswith(smallst) and smallst <> 'Depth':
                broadenedlist.append(fullst)
                realfieldbo = True
        if not realfieldbo:
            broadenedlist.append(smallst)

    return broadenedlist

def probeImportTextFile(importfilepathst, colseparatorst, lineseptypest):
    '''
    UNIT INFO
    Check the validity of the file to import.
    -show header-set based on used col-sep
    -check if separator is also used in the data  besides column-separator
    (which blocks the import)
    -count lines
    '''

    imfilenamest = os.path.basename(importfilepathst)
    curtimest = str(datetime.today())[:19]

    fileli = getListFromTextFile(importfilepathst, colseparatorst, 
    True, True, lineseptypest)
    choppedheaderli = fileli[0]
##    print choppedheaderli
    samplefieldli = pardb.getFieldList('','RELATIONS')
##    print samplefieldli
    fullheaderli = broadenHeaderList(choppedheaderli, samplefieldli )
##    print fullheaderli

    spacedfileli = getListFromTextFile(importfilepathst, colseparatorst, 
        False, True, lineseptypest)
    headerlenthli = spacedfileli[0]

    mest = 'Below are the full headers and column-widths based on given column-separator "' \
            + colseparatorst + '":\n\n'
    # below relid handled by database, other fields are import-identifiers

    linenumit = 0
    indata_separators_foundbo = False

    for lineli in fileli:
        if linenumit == 0:
            colit = 0

            for valst in lineli:
                mest += str(fullheaderli[colit]) + '  -  ' \
                        + str(len(headerlenthli[colit])) + '\n'
                colit += 1
            mest += '\nCheck that above header-list is present, normal and make sure that object1 and object2 \n' \
            + 'are present and of equal length (or your import will be corrupted).\n' \
            + 'Also check that MsDate is present if you want to import a calendar.\n'

        else:
            if len(lineli) > len(fullheaderli):
                indata_separators_foundbo = True
                mest += '\n>>>>>>    Line number ' + str(linenumit + 1) + \
                        ' contains one or more in-data separators. Please remove.   <<<<<<<'

##        elif linenumit > 0:   # skip header
##            colit = 0
##            for valst in lineli:
##                colit += 1

        linenumit += 1

    if indata_separators_foundbo == False:
        mest += '\nThere were no separators found inside the data...OK\n'
    else:
        mest += '\n\nRemove in-data separators from the text-file ' \
            + '(which may block or corrupt the import).'

    mest += '\n\nThe text-file contains ' + str(linenumit) + ' lines.\n'

    return mest


if __name__ == '__main__':
##    deffile = 'M:/EigThuis/Bestanden/Leuk/Hobbies/Programmeren/Python/MijnCode/PyKnow/other_files/kjx_export2.txt'
##    getListFromTextFile(deffile, ',')

    pass
